#coding:utf-8
#我的贪吃蛇
#2015.10.9

import random
import threading
import queue
import time
from tkinter import *
import tkinter.messagebox as msgbox

class Gui(Tk):
	"""
	GUI类，继承Tk
	属性
	gameover标志，消息队列，贪吃蛇
	食物，画布（参数控制界），按钮

	方法
	开始游戏（按下后开始游戏开始读取消息队列，并把该功能替换成重新开始，即刷新画布后，清空队列后再读取消息队列）
	退出（结束程序），help（弹出帮助），关于我们（弹出作者信息）
	"""
	def __init__(self,queue,food,snake):
		Tk.__init__(self)
		self.queue = queue
		self.geometry('1100x575+95+95')#窗体规格
		self.title('贪吃蛇')
		
		self.label=Label(self,width =1100 ,height = 575,bg="#3366FF")
		self.label.pack()
		#布置画布
		self.canvas = Canvas(self.label,width=850,height=550,bg="white")
		self.canvas.pack(padx=20,pady=20,side="left")
		
		#布置按钮
		self.begin = Label(self.label,text="开  始",width = 80,height = 1,bg="#0F4294",
			fg="white",font=("微软雅黑",25))
		self.begin.bind('<Button-1>',self.start_game)
		self.begin.pack(pady=40)

		self.End = Label(self.label,text="结  束",width = 80,height=1,bg="#0F4294",
			fg="white",font=("微软雅黑",25))
		self.End.bind('<Button-1>',self.exit_game)
		self.End.pack(pady=30)

		self.help = Label(self.label,text="帮  助",width = 80,height=1,bg="#0F4294",
			fg="white",font=("微软雅黑",25))
		self.help.bind('<Button-1>',self.help_game)
		self.help.pack(pady=30)

		self.about = Label(self.label,text="关 于 作 者",width = 80,height=1,bg="#0F4294",
			fg="white",font=("微软雅黑",24))
		self.about.bind('<Button-1>',self.about_us)
		self.about.pack(pady=30)	
		
		self.food = food
		self.snake = snake
		self.__init_food_snake()
		#初始化分数
		self.score = self.canvas.create_text(785,15,fill = "black",text = "score:0",font='Arial')

		#绑定方向键控制
		self.bind('<Key-Left>',self.__key_pressed)
		self.bind('<Key-Right>',self.__key_pressed)
		self.bind('<Key-Up>',self.__key_pressed)
		self.bind('<Key-Down>',self.__key_pressed)
	def start_game(self,even):
		#开始读取消息队列
		#开始进程
		#并把text改成重新开始
		if even.widget['text'] == '开  始':#两个空格
			even.widget['text'] = '重 新 开 始'	
			self.__queue_message()
			self.snake.snake_start()
		else:
			self.snake.is_game_over = True #强行结束死循环
			self.canvas.delete('all')
			self.snake.__init__(self.queue,food,op='again')
			self.food.__init__(self.queue)
			self.snake.score = 0
			self.score = self.canvas.create_text(785,15,fill = "black",text = "score:0",font='Arial')
			self.snake.is_game_over = False
			self.__init_food_snake()
			#初始化队列
			self.__queue_message()
	def exit_game(self,even):
		#因为会产生点击事件，所以写多一个参数
		self.snake.is_game_over = True #强行结束死循环
		self.snake.snake_stop()
		self.quit()
		return
	def help_game(self,even):
		#因为会产生点击事件，所以写多一个参数+
		msgbox.showinfo( "帮  助","← → ↑  ↓ 控制贪吃蛇移动,\n吃到水果得100分,吃到自己或撞到边界游戏结束")
		return
	def about_us(self,e):
		msgbox.showinfo( "关 于","本游戏由肥肥所写\n版权归肥肥兔拥有")

	def __init_food_snake(self):
		self.pic_food  = self.canvas.create_rectangle(self.food.exppos,fill = "red",outline = "green",width = 10)
		self.pic_snake = self.canvas.create_line(self.snake.position_list[0],
												 self.snake.position_list[1],
												 self.snake.position_list[2],
												 self.snake.position_list[3],
												 self.snake.position_list[4],fill = "orange",width = 16)
	def __key_pressed(self,e):
		self.snake.direction = e.keysym

	def __queue_message(self):
		#读取队列信息,负责对象间的的通信，以及实时更新水果的位置
		try:
			while 1:
				message = self.queue.get(block = False)
				if message.get('game_over') or message.get('game_over') == 0: #0分时不会结束的bug，用or语句修补
					self.snake.is_game_over = True
					msgbox.showinfo( "游 戏 结 束","Game over！\n你的得分为：" + str(message['game_over']))
					
					while not self.queue.empty():
						self.queue.get()#因为这是队列可能还没空，强行清空,否则会造成重复提示
				if message.get('move') :
					points = [x for point in message['move'] for x in point]
					self.canvas.coords(self.pic_snake,*points)
				if message.get('food') :
					self.canvas.coords(self.pic_food,*message['food'])
				if message.get('score') :
					self.canvas.itemconfigure(self.score,text = 'score{}'.format(message['score']))
		except queue.Empty:
			if  not self.snake.is_game_over :
				self.canvas.after(80,self.__queue_message)

class Food:
	"""
	食物类
	画布边界
	属性
	x,y，消息队列

	方法
	初始化食物，刷新位置
	"""
	def __init__(self,queue):
		self.queue = queue
		self.new_food()
	def new_food(self):
		x = random.randrange(40,835,10) #产生一个5到1090的随机数,递增为10
		y = random.randrange(40,535,10)
		self.position = x,y#真正的位置
		self.exppos = x-5,y-5,x+5,y+5
		self.queue.put({'food':self.exppos})

class Snake(threading.Thread):
	"""
	Snake类继承进程类
	属性
	画布边界
	由五个（x,y）构成的蛇身
	消息队列，当前方向

	方法
	开始一个进程负责蛇的自动移动，重写进程的run函数，一方面移动，一方面判断是否吃到水果
	移动函数
	判断键盘输入的函数，改变当前方向
	方向改变后改变蛇的方向
	"""
	def __init__(self,queue,food,op = None):
		if op ==  None:#第一次初始化
			threading.Thread.__init__(self)
		self.food = food
		self.queue = queue
		self.score = 0
		self.is_game_over = False
		self.add = False #是否增长
		x = random.randrange(60,850,10) #产生一个5到1090的随机数,递增为10
		y = random.randrange(60,550,10)
		self.position_list = [(x,y),(x-10,y),(x-20,y),(x-30,y),(x-40,y)]
		self.direction = 'Left'
	def snake_start(self):
		self.start()#开始进程

	def run(self):
		#进程自动调用的run函数
		while not self.is_game_over:
			self.queue.put({'move':self.position_list}) #放进队列
			time.sleep(0.5) #休眠
			self.__move() #移动小蛇

	def snake_stop(self):#结束所有进程
		self._delete()#结束进程

	def __move(self):
		new_point = self.__new_point()
		self.__check_game_over(new_point)
		#弄大吃水果的范围
		min_x = self.food.position[0] - 5
		min_y = self.food.position[1] - 5 
		max_x = self.food.position[0] + 5
		max_y = self.food.position[1] + 5
		x,y = new_point
		if ( min_x <= x <= max_x ) and (min_y <= y <= max_y):
			self.score += 100
			self.queue.put({'score':self.score}) #提交分数
			self.add = True
			self.food.new_food()
		elif self.add == True:
			self.add = False #初始化
		else:
			self.position_list.pop(0) #不变长则蛇尾点出队	
		self.position_list.append(new_point)

	def __new_point(self):
		last_x,last_y = self.position_list[-1]
		if self.direction == 'Up':
			new_point = (last_x,last_y - 10)
		elif self.direction == 'Down':
			new_point = (last_x,last_y + 10)
		elif self.direction == 'Left':
			new_point = (last_x - 10,last_y)
		elif self.direction == 'Right':
			new_point = (last_x + 10,last_y)
		return new_point

	def __check_game_over(self,snake_p):
		#snake_p是一个元组，代表蛇头
		x,y = snake_p[0],snake_p[1]
		if not (-10 < x < 860) or not (-10 <y < 535) or (snake_p in self.position_list):
			self.queue.put({'game_over':self.score})


q = queue.Queue()
food = Food(q)
snake = Snake(q,food)
gui = Gui(q,food,snake)
gui.mainloop()
